﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using DiscoveryLogic.UI.Layouts.Models;
using DiscoveryLogic.UI.Layouts.ContentScreens;

namespace DiscoveryLogic.UI.Layouts.Controls
{
    public partial class PieSlice : UserControl
    {

        #region Private Variables

            int id;

            double angle;
            double startAngle;
            
            MouseManager mouseEventManager;

            EnrollmentFund sliceFund;
            double sliceAmount;
            double sliceContribution;

            public Path slicePath;

            double totalPieAmount;

            private MissingFundIndicator missingFundIndicator;

            bool missingFund = false;
            bool isDummySlice = false;

            private double toolTipX;
            private double toolTipY;

        #endregion

        #region Constructors

            /// <summary>
            /// Creates a pie slice control
            /// </summary>
            public PieSlice() : this(0, 0, 0, null)
            {
            }

            /// <summary>
            /// Creates a pie slice control
            /// </summary>
            /// <param name="sliceAmount">The amount in dollars this slice represents</param>
            /// <param name="totalPieAmount">The total amount in dollars the pie chart represents</param>
            /// <param name="contribPercent">Contribution percent for the fund this slice represents</param>
            /// <param name="enrollmentFund">The fund this slice represents</param>
            public PieSlice(double sliceAmount, double totalPieAmount, double contribPercent, EnrollmentFund enrollmentFund)
            {
                // Required to initialize variables
                InitializeComponent();
                
                // Initialize variables
                startAngle = 0;
                this.SliceAmount = sliceAmount;
                this.totalPieAmount = totalPieAmount;
                angle = (sliceAmount / totalPieAmount) * 360;
                mouseEventManager = new MouseManager();
                
                // Add missing fund indicator
                missingFundIndicator = new MissingFundIndicator();
                missingFundIndicator.Opacity = 0;
                PieSliceCanvas.Children.Add(missingFundIndicator);

                // If a fund was passed in
                if (enrollmentFund != null)
                {
                    // Add a new fund to the canvas and then set its properties
                    sliceFund = enrollmentFund;
                    enrollmentFund.ParentSlice = this;
                    sliceFund.ResetPosition();
                    sliceFund.SwitchModes(false);
                    sliceFund.SetValue(Canvas.ZIndexProperty, 5);
                    sliceFund.ContributionPercent = contribPercent;
                    sliceFund.FundValue = sliceAmount;
                    // TODO: Periodically value out of range exception is thrown
                    PieSliceCanvas.Children.Add(sliceFund);
                }

                // Create slice
                CreateSlice();

                // Set event handlers for this pie slice
                SetEventHandlers(true);

                this.MouseEnter += new MouseEventHandler(OnMissingFundMouseEnter);
                this.MouseLeave += new MouseEventHandler(OnMissingFundMouseLeave);
            }

        #endregion

        #region Public Properties

            /// <summary>
            /// The angle of this pie slice.
            /// This is really end angle - start angle.
            /// </summary>
            public double Angle
            {
                get { return angle; }
                set { angle = value; }
            }

            /// <summary>
            /// The start angle (Where the slice starts).
            /// </summary>
            public double StartAngle
            {
                get { return startAngle; }
                set { startAngle = value; }
            }

            /// <summary>
            /// Unique Id of the slice
            /// </summary>
            public int Id
            {
                get { return id; }
                set { id = value; }
            }

            /// <summary>
            /// The fund that this slice represents
            /// </summary>
            public EnrollmentFund Fund
            {
                get { return sliceFund; }
                set 
                { 
                    sliceFund = value;
                    sliceFund.ParentSlice = this;

                    if (isDummySlice)
                    {
                        sliceFund.FundValue = 0;
                    }
                }
            }

            /// <summary>
            /// The amount in dollars this slice represents.
            /// When this is set, the angle variable is recalculated.
            /// </summary>
            public double SliceAmount
            {
                get { return sliceAmount; }
                set
                {
                    sliceAmount = value;
                    angle = (sliceAmount / totalPieAmount) * 360;

                    // If the fund is not null, update the value
                    if (sliceFund != null)
                    {
                        sliceFund.FundValue = value;
                    }
                }
            }

            /// <summary>
            /// Set contribution percent amomunt.
            /// </summary>
            public double SliceContribution
            {
                get { return sliceContribution; }
                set 
                {
                    sliceContribution = value;

                    if (sliceFund != null)
                    {
                        sliceFund.ContributionPercent = value;
                    }
                }
            }

            /// <summary>
            /// Set total amount in dollars the pie chart represents.
            /// When this is set, the angle variable is reset.
            /// </summary>
            public double TotalPieAmount
            {
                get { return totalPieAmount; }
                set {
                    totalPieAmount = value;
                    angle = (sliceAmount / totalPieAmount) * 360;
                }
            }

            public bool MissingFund
            {
                get { return missingFund; }
                set 
                {
                    missingFund = value;
                    ShowMissingFundIndicator(missingFund);
                }
            }

            public bool IsDummySlice
            {
                get { return isDummySlice; }
                set 
                { 
                    isDummySlice = value;

                    if (sliceFund != null)
                    {
                        if (isDummySlice == true)
                        {
                            sliceFund.FundValue = 0;
                            SetDummyColor();
                        }
                        else
                        {
                            ResetColor();
                        }
                    }
                }
            }

        #endregion

        #region Public Methods

            /// <summary>
            /// Set the path data for this pie slice.
            /// Also sets the position of the fund icon on the slice and the
            /// position of the mouse over animation of the slice.
            /// </summary>
            /// <param name="pathGeometry">The data for the path object of the slice</param>
            /// <param name="toolTipPoint">Point representing where the fund icon will be</param>
            /// <param name="animationPoint">Coordinates representing where the mouse over animation will translate the slice to</param>
            /// <param name="strokeThickness">Set stroke thickness of the slice</param>
            /// <param name="pieCenter">Point representing centre of the pie</param>
            public void SetSlice(PathGeometry pathGeometry, Point toolTipPoint, Point animationPoint, double strokeThickness, Point pieCenter)
            {
                // If there is no slice data, that means the slice is being drawn for the first time.
                // Start the animation to scale the slice from 0 to 1
                if (slicePath.Data == null)
                {
                    try
                    {
                        GrowSlice.Begin();
                    }
                    catch (Exception ex)
                    {
                        string err = ex.Message;
                    }
                }

                // Set the path data for the pie slice
                slicePath.Data = pathGeometry;

                // Set the x and y for the mouse over animation
                LiftSliceKeyFrameX.Value = animationPoint.X;
                LiftSliceKeyFrameY.Value = animationPoint.Y;
                slicePath.StrokeThickness = strokeThickness;

                // This if statement is to fix offsets observed when
                // setting position of the fund icon on the slice
                if (sliceFund != null)
                {
                    bool onRightSide = true;
                    bool onTopSide = true;

                    if (toolTipPoint.X < pieCenter.X)
                    {
                        onRightSide = false;
                    }
                    if (toolTipPoint.Y > pieCenter.Y)
                    {
                        onTopSide = false;
                    }

                    if (onRightSide == true)
                    {
                        sliceFund.SetValue(Canvas.LeftProperty, toolTipPoint.X);
                        toolTipX = toolTipPoint.X;

                        missingFundIndicator.SetValue(Canvas.LeftProperty, toolTipPoint.X - 20);                        
                    }
                    else
                    {
                        sliceFund.SetValue(Canvas.LeftProperty, toolTipPoint.X - 40);
                        toolTipX = toolTipPoint.X - 40;

                        missingFundIndicator.SetValue(Canvas.LeftProperty, toolTipPoint.X);

                    }

                    if (onTopSide == true)
                    {
                        sliceFund.SetValue(Canvas.TopProperty, toolTipPoint.Y - 40);
                        toolTipY = toolTipPoint.Y - 40;

                        missingFundIndicator.SetValue(Canvas.TopProperty, toolTipPoint.Y);
                    }
                    else
                    {
                        sliceFund.SetValue(Canvas.TopProperty, toolTipPoint.Y);
                        toolTipY = toolTipPoint.Y;

                        missingFundIndicator.SetValue(Canvas.TopProperty, toolTipPoint.Y - 20);
                    }

                    // Set position of the pie text
                    sliceFund.SetPieTextPosition(onRightSide);
                }

            }

            /// <summary>
            /// Animation on hover over slice.
            /// </summary>
            public void LiftSlice()
            {                
                // Only show the animation if there are more than 2 slices
                if (sliceAmount != totalPieAmount)
                {
                    AnimLiftSlice.Begin();
                }
            }

            /// <summary>
            /// Animation on mouse leave slice
            /// </summary>
            public void LowerSlice()
            {
                if (sliceAmount != totalPieAmount)
                {
                    AnimLowerSlice.Begin();
                }
            }

            /// <summary>
            /// "Highlights" the slice on certain events.
            /// Changes the color of the slice.
            /// </summary>
            public void SetHighlightColor()
            {
                slicePath.Fill = CreateBrush("#FFFFFF99");
            }

            /// <summary>
            /// Changes the color of the slice when it is dragged
            /// off bounds (to be removed from pie chart)
            /// </summary>
            public void SetOffColor()
            {                
                slicePath.Fill = CreateBrush("#FFB8002E");
            }

            public void SetDummyColor()
            {
                slicePath.Fill = CreateBrush("#FF333333");
            }

            public void SetOpaqueColor()
            {
                //slicePath.SetValue(Path.FillProperty, sliceFund.GetFundColor());
                string color = sliceFund.GetFundColor();
                string lighterColor = sliceFund.GetFundColor();

                if (color == "#FFFFB135")
                {
                    lighterColor = "#FFFFCE80";
                }
                else if (color == "#FF4AB572")
                {
                    lighterColor = "#FF80CB9C";
                }
                else if (color == "#FFFF5F25")
                {
                    lighterColor = "#FFFF8457";
                }
                else if (color == "#FF12574C")
                {
                    lighterColor = "#FF1C8271";
                }
                else if (color == "#FF5D1E5D")
                {
                    lighterColor = "#FF963196";
                }
                else if (color == "#FFC436BF")
                {
                    lighterColor = "#FF9C2B98";
                }

                GradientStop stop1 = new GradientStop();
                stop1.Color = Utils.HexToColor(lighterColor);
                stop1.Offset = 1;
                GradientStop stop2 = new GradientStop();
                stop2.Color = Utils.HexToColor(sliceFund.GetFundColor());
                stop2.Offset = 0;
                LinearGradientBrush gradBrush = new LinearGradientBrush();
                gradBrush.EndPoint = new Point(0.261,0.084);
                gradBrush.StartPoint = new Point(0.741,0.917);
                gradBrush.GradientStops.Add(stop1);
                gradBrush.GradientStops.Add(stop2);

                slicePath.Fill = gradBrush;

            }

            /// <summary>
            /// Reset the pie slice to the color of the fund type
            /// </summary>
            public void ResetColor()
            {
                if (sliceFund != null)
                {                    
                    slicePath.Fill = CreateBrush(sliceFund.GetFundColor());                    
                }
                else
                {
                    slicePath.Fill = CreateBrush("#FF1B60AC");
                }                
            }

            /// <summary>
            /// Change opacity of the slice
            /// </summary>
            /// <param name="opacity"></param>
            public void SetOpacity(double opacity)
            {
                ChangeSliceOpacity.Stop();
                ChangeSliceOpacity_KeyFrame.SetValue(SplineDoubleKeyFrame.ValueProperty, opacity);
                ChangeSliceOpacity.Begin();
            }

            /// <summary>
            /// Reset the position of the pie slice
            /// </summary>
            public void ResetPosition()
            {
                this.SetValue(Canvas.LeftProperty, 0.0);
                this.SetValue(Canvas.TopProperty, 0.0);
            }

            /// <summary>
            /// Resets the position of the fund to the tooltip position
            /// </summary>
            public void ResetFundPosition()
            {
                if (sliceFund != null)
                {
                    sliceFund.SetValue(Canvas.LeftProperty, toolTipX);
                    sliceFund.SetValue(Canvas.TopProperty, toolTipY);
                }
            }
            
            /// <summary>
            /// Remove the fund from the canvas
            /// </summary>
            /// <returns></returns>
            public EnrollmentFund DetachFund()
            {
                sliceFund.SwitchModes(true);
                sliceFund.SetValue(Canvas.LeftProperty, 0.0);
                sliceFund.SetValue(Canvas.TopProperty, 0.0);
                sliceFund.ParentSlice = null;
                SetEventHandlers(false);
                if(PieSliceCanvas.Children.Contains(sliceFund))
                {
                    PieSliceCanvas.Children.Remove(sliceFund);
                }
                return sliceFund;
            }

            public void HideFundIcon(bool hide)
            {
                if (hide == true)
                {
                    sliceFund.Visibility = Visibility.Collapsed;
                }
                else
                {
                    sliceFund.Visibility = Visibility.Visible;
                }
            }

            public void RemoveAllHandlers()
            {
                SetEventHandlers(false);
            }

            public void ShowMissingFundIndicatorToolTip(bool show)
            {
                if (show)
                {
                    missingFundIndicator.ShowToolTip();
                }
                else
                {
                    missingFundIndicator.HideToolTip();
                }
            }

        #endregion

        #region Helper Methods

            private void ShowMissingFundIndicator(bool show)
            {
                if (show)
                {
                    missingFundIndicator.Opacity = 1;
                    missingFundIndicator.SetValue(Canvas.ZIndexProperty, 1);
                }
                else
                {
                    missingFundIndicator.Opacity = 0;
                    missingFundIndicator.SetValue(Canvas.ZIndexProperty, 0);
                }
            }

            /// <summary>
            /// Sets the event handlers for certain events of this slice
            /// </summary>
            private void SetEventHandlers(bool status)
            {
                if (status)
                {
                    // Set mouse events for this pie slice
                    sliceFund.MouseEnter += mouseEventManager.PieSliceMouseEnter;
                    sliceFund.MouseLeave += mouseEventManager.PieSliceMouseLeave;
                    sliceFund.MouseLeftButtonDown += mouseEventManager.PieSliceMouseDown;
                    sliceFund.MouseLeftButtonUp += mouseEventManager.PieSliceMouseUp;
                    sliceFund.MouseMove += mouseEventManager.DragMouseMove;
                    mouseEventManager.dragMove += new MouseManager.DragMoveEventHandler(OnDrag);
                    mouseEventManager.mouseUp += new MouseEventHandler(OnMouseButtonUp);
                    mouseEventManager.mouseDown += new EventHandler(OnMouseButtonDown);
                }
                else
                {                  
                    // Set mouse events for this pie slice                    
                    sliceFund.MouseEnter -= mouseEventManager.PieSliceMouseEnter;
                    sliceFund.MouseLeave -= mouseEventManager.PieSliceMouseLeave;
                    sliceFund.MouseLeftButtonDown -= mouseEventManager.PieSliceMouseDown;
                    sliceFund.MouseLeftButtonUp -= mouseEventManager.PieSliceMouseUp;
                    sliceFund.MouseMove -= mouseEventManager.DragMouseMove;
                    mouseEventManager.dragMove -= new MouseManager.DragMoveEventHandler(OnDrag);
                    mouseEventManager.mouseUp -= new MouseEventHandler(OnMouseButtonUp);
                    mouseEventManager.mouseDown -= new EventHandler(OnMouseButtonDown);
                }
            }

            /// <summary>
            /// Create a path object that will represent the slice.
            /// Later the path data will be set in another method by
            /// PieChart control
            /// </summary>
            private void CreateSlice()
            {
                // Path object for the pie slice
                slicePath = new Path();
                slicePath.SetValue(Canvas.NameProperty, "PieSlice");
                slicePath.Stroke = new SolidColorBrush(Colors.White);
                slicePath.StrokeThickness = 1;
             
                // Add a transform to it
                TransformGroup tg = new TransformGroup();
                
                TranslateTransform translate = new TranslateTransform();
                translate.SetValue(Canvas.NameProperty, "SliceTranslate");
                translate.X = 0;
                translate.Y = 0;
                
                ScaleTransform scale = new ScaleTransform();
                translate.SetValue(Canvas.NameProperty, "SliceScale");
                scale.CenterX = 200;
                scale.CenterY = 200;
                scale.ScaleX = 1;
                scale.ScaleY = 1;
                
                tg.Children.Add(translate);
                tg.Children.Add(scale);
                slicePath.RenderTransform = tg;

                // Do fill
                if (sliceFund != null)
                {
                    slicePath.Fill = CreateBrush(sliceFund.GetFundColor());
                }
                else
                {
                    slicePath.Fill = CreateBrush("#FF1B60AC");
                }

                // Add slice to canvas
                PieSliceCanvas.Children.Add(slicePath);
            }

            /// <summary>
            /// Create a gradient brush given a color.
            /// Will create a gradient from color passed in to a 
            /// lighter shade of the same color.
            /// </summary>
            /// <param name="color"></param>
            /// <returns></returns>
            private Brush CreateBrush(string color)
            {
                string lighterColor = color;

                if (color == "#FFFFB135")
                {
                    lighterColor = "#BBFFEBCC";
                }
                else if (color == "#FF4AB572")
                {
                    lighterColor = "#BB92D3AA";
                }
                else if (color == "#FFFF5F25")
                {
                    lighterColor = "#BBFFA98A";
                }
                else if (color == "#FF12574C")
                {
                    lighterColor = "#BB1C8271";
                }
                else if (color == "#FF5D1E5D")
                {
                    lighterColor = "#BBA937A9";
                }
                else if (color == "#FFC436BF")
                {
                    lighterColor = "#BBE298DF";
                }

                GradientStop stop1 = new GradientStop();
                stop1.Color = Utils.HexToColor(lighterColor);
                stop1.Offset = 1;
                GradientStop stop2 = new GradientStop();
                stop2.Color = Utils.HexToColor(color);
                stop2.Offset = 0;
                LinearGradientBrush gradBrush = new LinearGradientBrush();
                gradBrush.EndPoint = new Point(0.261,0.084);
                gradBrush.StartPoint = new Point(0.741,0.917);
                gradBrush.GradientStops.Add(stop1);
                gradBrush.GradientStops.Add(stop2);
                return gradBrush;
            }            

            /// <summary>
            /// Create a brush to mimic a shadow
            /// </summary>
            /// <returns></returns>
            private RadialGradientBrush CreateShadowBrush()
            {
                // Add brush to fill property of shadow
                RadialGradientBrush rgb = new RadialGradientBrush();
                GradientStop gs1 = new GradientStop();
                GradientStop gs2 = new GradientStop();
                GradientStop gs3 = new GradientStop();

                gs1.Color = Utils.HexToColor("#3F000000");
                gs1.Offset = 1;
                gs2.Color = Utils.HexToColor("#00000000");
                gs2.Offset = 1;
                gs2.Color = Colors.Transparent;
                gs3.Offset = 1;

                rgb.GradientStops.Add(gs1);
                rgb.GradientStops.Add(gs2);
                rgb.GradientStops.Add(gs3);

                return rgb;
            }
           
        #endregion

        #region Event Handlers

            /// <summary>
            /// On drag of the slice, move the slice and
            /// raise sliceDrag event.
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            /// <param name="mouseArgs"></param>
            private void OnDrag(object sender, DiscoveryLogic.UI.Layouts.Models.CustomEventArgs e, MouseEventArgs mouseArgs)
            {
                sliceFund.SetValue(Canvas.LeftProperty, e.DragX);
                sliceFund.SetValue(Canvas.TopProperty, e.DragY);

                if (sliceDrag != null)
                {
                    e.Id = this.id;
                    sliceDrag(this, mouseArgs);
                }
            }

            /// <summary>
            /// On mouse up of the slice, raise event
            /// pieSliceMouseUp. This is handled in the PieChart control
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnMouseButtonUp(object sender, MouseEventArgs e)
            {
                if (pieSliceMouseUp != null)
                {
                    pieSliceMouseUp(sender, e);
                }
            }

            /// <summary>
            /// On mouse up of the slice, raise event
            /// pieSliceMouseDown. This is handled in the PieChart control
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnMouseButtonDown(object sender, EventArgs e)
            {
                if (pieSliceMouseDown != null)
                {
                    pieSliceMouseDown(sender, e);
                }
            }

            private void OnMissingFundMouseEnter(object sender, MouseEventArgs e)
            {
                if (MissingFund == true && pieSliceMouseEnter != null)
                {
                    pieSliceMouseEnter(this, e);
                }
            }

            private void OnMissingFundMouseLeave(object sender, MouseEventArgs e)
            {
                if (MissingFund == true && pieSliceMouseLeave != null)
                {
                    pieSliceMouseLeave(this, e);
                }
            }

        
        #endregion

        #region Events

            public event EventHandler sliceDrag;
            public event MouseEventHandler pieSliceMouseUp;
            public event EventHandler pieSliceMouseDown;
            public event MouseEventHandler pieSliceMouseEnter;
            public event MouseEventHandler pieSliceMouseLeave;

        #endregion

    }
}
